home *** CD-ROM | disk | FTP | other *** search
/ Power Programmierung 2 / Power-Programmierung CD 2 (Tewi)(1994).iso / gnu / djgpp / src / texinfo-.1 / info / info-uti.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-09-26  |  16.6 KB  |  658 lines

  1. /* info-utils.c -- Useful functions for manipulating Info file quirks. */
  2.  
  3. /* This file is part of GNU Info, a program for reading online documentation
  4.    stored in Info format.
  5.  
  6.    Copyright (C) 1993 Free Software Foundation, Inc.
  7.  
  8.    This program is free software; you can redistribute it and/or modify
  9.    it under the terms of the GNU General Public License as published by
  10.    the Free Software Foundation; either version 2, or (at your option)
  11.    any later version.
  12.  
  13.    This program is distributed in the hope that it will be useful,
  14.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  15.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16.    GNU General Public License for more details.
  17.  
  18.    You should have received a copy of the GNU General Public License
  19.    along with this program; if not, write to the Free Software
  20.    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  21.  
  22.    Written by Brian Fox (bfox@ai.mit.edu). */
  23.  
  24. #include <stdio.h>        /* For "NULL".  Yechhh! */
  25. #include <ctype.h>
  26. #include <sys/types.h>
  27. #include <sys/stat.h>
  28. #include "info-utils.h"
  29.  
  30. /* When non-zero, various display and input functions handle ISO Latin
  31.    character sets correctly. */
  32. #if defined (__GO32__)
  33. int ISO_Latin_p = 1;
  34. #else /* __GO32__ */
  35. int ISO_Latin_p = 0;
  36. #endif /* __GO32__ */
  37.  
  38. /* Variable which holds the most recent filename parsed as a result of
  39.    calling info_parse_xxx (). */
  40. char *info_parsed_filename = (char *)NULL;
  41.  
  42. /* Variable which holds the most recent nodename parsed as a result of
  43.    calling info_parse_xxx (). */
  44. char *info_parsed_nodename = (char *)NULL;
  45.  
  46. /* Functions to remember a filename or nodename for later return. */
  47. static void save_filename (), saven_filename ();
  48. static void save_nodename (), saven_nodename ();
  49.  
  50. /* How to get a reference (either menu or cross). */
  51. static REFERENCE **info_references_internal ();
  52.  
  53. /* Parse the filename and nodename out of STRING.  If STRING doesn't
  54.    contain a filename (i.e., it is NOT (FILENAME)NODENAME) then set
  55.    INFO_PARSED_FILENAME to NULL.  If second argument NEWLINES_OKAY is
  56.    non-zero, it says to allow the nodename specification to cross a
  57.    newline boundary (i.e., only `,', `.', or `TAB' can end the spec). */
  58. void
  59. info_parse_node (string, newlines_okay)
  60.      char *string;
  61.      int newlines_okay;
  62. {
  63.   register int i = 0;
  64.  
  65.   /* Default the answer. */
  66.   save_filename ((char *)NULL);
  67.   save_nodename ((char *)NULL);
  68.  
  69.   /* Special case of nothing passed.  Return nothing. */
  70.   if (!string || !*string)
  71.     return;
  72.  
  73.   string += skip_whitespace (string);
  74.  
  75.   /* Check for (FILENAME)NODENAME. */
  76.   if (*string == '(')
  77.     {
  78.       i = 0;
  79.       /* Advance past the opening paren. */
  80.       string++;
  81.  
  82.       /* Find the closing paren. */
  83.       while (string[i] && string[i] != ')')
  84.     i++;
  85.  
  86.       /* Remember parsed filename. */
  87.       saven_filename (string, i);
  88.  
  89.       /* Point directly at the nodename. */
  90.       string += i;
  91.  
  92.       if (*string)
  93.     string++;
  94.     }
  95.  
  96.   /* Parse out nodename. */
  97.   i = skip_node_characters (string, newlines_okay);
  98.   saven_nodename (string, i);
  99.   canonicalize_whitespace (info_parsed_nodename);
  100.   if (info_parsed_nodename && !*info_parsed_nodename)
  101.     {
  102.       free (info_parsed_nodename);
  103.       info_parsed_nodename = (char *)NULL;
  104.     }
  105. }
  106.  
  107. /* Return the node addressed by LABEL in NODE (usually one of "Prev:",
  108.    "Next:", "Up:", "File:", or "Node:".  After a call to this function,
  109.    the global INFO_PARSED_NODENAME and INFO_PARSED_FILENAME contain
  110.    the information. */
  111. void
  112. info_parse_label (label, node)
  113.      char *label;
  114.      NODE *node;
  115. {
  116.   register int i;
  117.   char *nodeline;
  118.  
  119.   /* Default answer to failure. */
  120.   save_nodename ((char *)NULL);
  121.   save_filename ((char *)NULL);
  122.  
  123.   /* Find the label in the first line of this node. */
  124.   nodeline = node->contents;
  125.   i = string_in_line (label, nodeline);
  126.  
  127.   if (i == -1)
  128.     return;
  129.  
  130.   nodeline += i;
  131.   nodeline += skip_whitespace (nodeline);
  132.   info_parse_node (nodeline, DONT_SKIP_NEWLINES);
  133. }
  134.  
  135. /* **************************************************************** */
  136. /*                                    */
  137. /*            Finding and Building Menus                */
  138. /*                                    */
  139. /* **************************************************************** */
  140.  
  141. /* Return a NULL terminated array of REFERENCE * which represents the menu
  142.    found in NODE.  If there is no menu in NODE, just return a NULL pointer. */
  143. REFERENCE **
  144. info_menu_of_node (node)
  145.      NODE *node;
  146. {
  147.   long position;
  148.   SEARCH_BINDING search;
  149.   REFERENCE **menu = (REFERENCE **)NULL;
  150.  
  151.   search.buffer = node->contents;
  152.   search.start = 0;
  153.   search.end = node->nodelen;
  154.   search.flags = S_FoldCase;
  155.  
  156.   /* Find the start of the menu. */
  157.   position = search_forward (INFO_MENU_LABEL, &search);
  158.  
  159.   if (position == -1)
  160.     return ((REFERENCE **) NULL);
  161.  
  162.   /* We have the start of the menu now.  Glean menu items from the rest
  163.      of the node. */
  164.   search.start = position + strlen (INFO_MENU_LABEL);
  165.   search.start += skip_line (search.buffer + search.start);
  166.   search.start--;
  167.   menu = info_menu_items (&search);
  168.   return (menu);
  169. }
  170.  
  171. /* Return a NULL terminated array of REFERENCE * which represents the cross
  172.    refrences found in NODE.  If there are no cross references in NODE, just
  173.    return a NULL pointer. */
  174. REFERENCE **
  175. info_xrefs_of_node (node)
  176.      NODE *node;
  177. {
  178.   SEARCH_BINDING search;
  179.  
  180.   search.buffer = node->contents;
  181.   search.start = 0;
  182.   search.end = node->nodelen;
  183.   search.flags = S_FoldCase;
  184.  
  185.   return (info_xrefs (&search));
  186. }
  187.  
  188. /* Glean menu entries from BINDING->buffer + BINDING->start until we
  189.    have looked at the entire contents of BINDING.  Return an array
  190.    of REFERENCE * that represents each menu item in this range. */
  191. REFERENCE **
  192. info_menu_items (binding)
  193.      SEARCH_BINDING *binding;
  194. {
  195.   return (info_references_internal (INFO_MENU_ENTRY_LABEL, binding));
  196. }
  197.  
  198. /* Glean cross references from BINDING->buffer + BINDING->start until
  199.    BINDING->end.  Return an array of REFERENCE * that represents each
  200.    cross reference in this range. */
  201. REFERENCE **
  202. info_xrefs (binding)
  203.      SEARCH_BINDING *binding;
  204. {
  205.   return (info_references_internal (INFO_XREF_LABEL, binding));
  206. }
  207.  
  208. /* Glean cross references or menu items from BINDING.  Return an array
  209.    of REFERENCE * that represents the items found. */
  210. static REFERENCE **
  211. info_references_internal (label, binding)
  212.      char *label;
  213.      SEARCH_BINDING *binding;
  214. {
  215.   SEARCH_BINDING search;
  216.   REFERENCE **refs = (REFERENCE **)NULL;
  217.   int refs_index = 0, refs_slots = 0;
  218.   int searching_for_menu_items = 0;
  219.   long position;
  220.  
  221.   search.buffer = binding->buffer;
  222.   search.start = binding->start;
  223.   search.end = binding->end;
  224.   search.flags = S_FoldCase | S_SkipDest;
  225.  
  226.   searching_for_menu_items = (stricmp (label, INFO_MENU_ENTRY_LABEL) == 0);
  227.  
  228.   while ((position = search_forward (label, &search)) != -1)
  229.     {
  230.       int offset, start;
  231.       char *refdef;
  232.       REFERENCE *entry;
  233.  
  234.       search.start = position;
  235.       search.start += skip_whitespace (search.buffer + search.start);
  236.       start = search.start - binding->start;
  237.       refdef = search.buffer + search.start;
  238.       offset = string_in_line (":", refdef);
  239.  
  240.       /* When searching for menu items, if no colon, there is no
  241.      menu item on this line. */
  242.       if (offset == -1)
  243.     {
  244.       if (searching_for_menu_items)
  245.         continue;
  246.       else
  247.         {
  248.           int temp;
  249.  
  250.           temp = skip_line (refdef);
  251.           offset = string_in_line (":", refdef + temp);
  252.           if (offset == -1)
  253.         continue;    /* Give up? */
  254.           else
  255.         offset += temp;
  256.         }
  257.     }
  258.  
  259.       entry = (REFERENCE *)xmalloc (sizeof (REFERENCE));
  260.       entry->filename = (char *)NULL;
  261.       entry->nodename = (char *)NULL;
  262.       entry->label = (char *)xmalloc (offset);
  263.       strncpy (entry->label, refdef, offset - 1);
  264.       entry->label[offset - 1] = '\0';
  265.       canonicalize_whitespace (entry->label);
  266.  
  267.       refdef += offset;
  268.       entry->start = start;
  269.       entry->end = refdef - binding->buffer;
  270.  
  271.       /* If this reference entry continues with another ':' then the
  272.      nodename is the same as the label. */
  273.       if (*refdef == ':')
  274.     {
  275.       entry->nodename = savestring (entry->label);
  276.     }
  277.       else
  278.     {
  279.       /* This entry continues with a specific nodename.  Parse the
  280.          nodename from the specification. */
  281.  
  282.       refdef += skip_whitespace_and_newlines (refdef);
  283.  
  284.       if (searching_for_menu_items)
  285.         info_parse_node (refdef, DONT_SKIP_NEWLINES);
  286.       else
  287.         info_parse_node (refdef, SKIP_NEWLINES);
  288.  
  289.       if (info_parsed_filename)
  290.         entry->filename = savestring (info_parsed_filename);
  291.  
  292.       if (info_parsed_nodename)
  293.         entry->nodename = savestring (info_parsed_nodename);
  294.     }
  295.  
  296.       add_pointer_to_array
  297.     (entry, refs_index, refs, refs_slots, 50, REFERENCE *);
  298.     }
  299.   return (refs);
  300. }
  301.  
  302. /* Get the entry associated with LABEL in MENU.  Return a pointer to the
  303.    REFERENCE if found, or NULL. */
  304. REFERENCE *
  305. info_get_labeled_reference (label, references)
  306.      char *label;
  307.      REFERENCE **references;
  308. {
  309.   register int i;
  310.   REFERENCE *entry;
  311.  
  312.   for (i = 0; references && (entry = references[i]); i++)
  313.     {
  314.       if (strcmp (label, entry->label) == 0)
  315.     return (entry);
  316.     }
  317.   return ((REFERENCE *)NULL);
  318. }
  319.  
  320. /* A utility function for concatenating REFERENCE **.  Returns a new
  321.    REFERENCE ** which is the concatenation of REF1 and REF2.  The REF1
  322.    and REF2 arrays are freed, but their contents are not. */
  323. REFERENCE **
  324. info_concatenate_references (ref1, ref2)
  325.      REFERENCE **ref1, **ref2;
  326. {
  327.   register int i, j;
  328.   REFERENCE **result;
  329.   int size;
  330.  
  331.   /* With one argument passed as NULL, simply return the other arg. */
  332.   if (!ref1)
  333.     return (ref2);
  334.   else if (!ref2)
  335.     return (ref1);
  336.  
  337.   /* Get the total size of the slots that we will need. */
  338.   for (i = 0; ref1[i]; i++);
  339.   size = i;
  340.   for (i = 0; ref2[i]; i++);
  341.   size += i;
  342.  
  343.   result = (REFERENCE **)xmalloc ((1 + size) * sizeof (REFERENCE *));
  344.  
  345.   /* Copy the contents over. */
  346.   for (i = 0; ref1[i]; i++)
  347.     result[i] = ref1[i];
  348.  
  349.   j = i;
  350.   for (i = 0; ref2[i]; i++)
  351.     result[j++] = ref2[i];
  352.  
  353.   result[j] = (REFERENCE *)NULL;
  354.   free (ref1);
  355.   free (ref2);
  356.   return (result);
  357. }
  358.  
  359. /* Free the data associated with REFERENCES. */
  360. void
  361. info_free_references (references)
  362.      REFERENCE **references;
  363. {
  364.   register int i;
  365.   REFERENCE *entry;
  366.  
  367.   if (references)
  368.     {
  369.       for (i = 0; references && (entry = references[i]); i++)
  370.     {
  371.       maybe_free (entry->label);
  372.       maybe_free (entry->filename);
  373.       maybe_free (entry->nodename);
  374.  
  375.       free (entry);
  376.     }
  377.  
  378.       free (references);
  379.     }
  380. }
  381.  
  382. /* Search for sequences of whitespace or newlines in STRING, replacing
  383.    all such sequences with just a single space.  Remove whitespace from
  384.    start and end of string. */
  385. void
  386. canonicalize_whitespace (string)
  387.      char *string;
  388. {
  389.   register int i, j;
  390.   int len, whitespace_found, whitespace_loc;
  391.   char *temp;
  392.  
  393.   if (!string)
  394.     return;
  395.  
  396.   len = strlen (string);
  397.   temp = (char *)xmalloc (1 + len);
  398.  
  399.   /* Search for sequences of whitespace or newlines.  Replace all such
  400.      sequences in the string with just a single space. */
  401.  
  402.   whitespace_found = 0;
  403.   for (i = 0, j = 0; string[i]; i++)
  404.     {
  405.       if (whitespace_or_newline (string[i]))
  406.     {
  407.       whitespace_found++;
  408.       whitespace_loc = i;
  409.       continue;
  410.     }
  411.       else
  412.     {
  413.       if (whitespace_found && whitespace_loc)
  414.         {
  415.           whitespace_found = 0;
  416.           temp[j++] = ' ';
  417.         }
  418.  
  419.       temp[j++] = string[i];
  420.     }
  421.     }
  422.  
  423.   /* Kill trailing whitespace. */
  424.   if (j && whitespace (temp[j - 1]))
  425.     j--;
  426.  
  427.   temp[j] = '\0';
  428.   strcpy (string, temp);
  429.   free (temp);
  430. }
  431.  
  432. /* String representation of a char returned by printed_representation (). */
  433. static char the_rep[10];
  434.  
  435. /* Return a pointer to a string which is the printed representation
  436.    of CHARACTER if it were printed at HPOS. */
  437. char *
  438. printed_representation (character, hpos)
  439.      unsigned char character;
  440.      int hpos;
  441. {
  442.   register int i = 0;
  443.   int printable_limit;
  444.  
  445.   if (ISO_Latin_p)
  446.     printable_limit = 160;
  447.   else
  448.     printable_limit = 127;
  449.  
  450.   if (character == '\177')
  451.     {
  452.       the_rep[i++] = '^';
  453.       the_rep[i++] = '?';
  454.     }
  455.   else if (iscntrl (character))
  456.     {
  457.       switch (character)
  458.     {
  459.     case '\r':
  460.     case '\n':
  461.       the_rep[i++] = character;
  462.       break;
  463.  
  464.     case '\t':
  465.       {
  466.         int tw;
  467.  
  468.         tw = ((hpos + 8) & 0xf8) - hpos;
  469.         while (i < tw)
  470.           the_rep[i++] = ' ';
  471.       }
  472.       break;
  473.  
  474.     default:
  475.       the_rep[i++] = '^';
  476.       the_rep[i++] = (character | 0x40);
  477.     }
  478.     }
  479.   else if (character > printable_limit)
  480.     {
  481.       sprintf (the_rep + i, "\\%0o", character);
  482.       i = strlen (the_rep);
  483.     }
  484.   else
  485.     the_rep[i++] = character;
  486.  
  487.   the_rep[i] = '\0';
  488.  
  489.   return (the_rep);
  490. }
  491.  
  492.  
  493. /* **************************************************************** */
  494. /*                                    */
  495. /*            Functions Static To This File            */
  496. /*                                    */
  497. /* **************************************************************** */
  498.  
  499. /* Amount of space allocated to INFO_PARSED_FILENAME via xmalloc (). */
  500. static int parsed_filename_size = 0;
  501.  
  502. /* Amount of space allocated to INFO_PARSED_NODENAME via xmalloc (). */
  503. static int parsed_nodename_size = 0;
  504.  
  505. static void save_string (), saven_string ();
  506.  
  507. /* Remember FILENAME in PARSED_FILENAME.  An empty FILENAME is translated
  508.    to a NULL pointer in PARSED_FILENAME. */
  509. static void
  510. save_filename (filename)
  511.      char *filename;
  512. {
  513.   save_string (filename, &info_parsed_filename, &parsed_filename_size);
  514. }
  515.  
  516. /* Just like save_filename (), but you pass the length of the string. */
  517. static void
  518. saven_filename (filename, len)
  519.      char *filename;
  520.      int len;
  521. {
  522.   saven_string (filename, len,
  523.         &info_parsed_filename, &parsed_filename_size);
  524. }
  525.  
  526. /* Remember NODENAME in PARSED_NODENAME.  An empty NODENAME is translated
  527.    to a NULL pointer in PARSED_NODENAME. */
  528. static void
  529. save_nodename (nodename)
  530.      char *nodename;
  531. {
  532.   save_string (nodename, &info_parsed_nodename, &parsed_nodename_size);
  533. }
  534.  
  535. /* Just like save_nodename (), but you pass the length of the string. */
  536. static void
  537. saven_nodename (nodename, len)
  538.      char *nodename;
  539.      int len;
  540. {
  541.   saven_string (nodename, len,
  542.         &info_parsed_nodename, &parsed_nodename_size);
  543. }
  544.  
  545. /* Remember STRING in STRING_P.  STRING_P should currently have STRING_SIZE_P
  546.    bytes allocated to it.  An empty STRING is translated to a NULL pointer
  547.    in STRING_P. */
  548. static void
  549. save_string (string, string_p, string_size_p)
  550.      char *string;
  551.      char **string_p;
  552.      int *string_size_p;
  553. {
  554.   if (!string || !*string)
  555.     {
  556.       if (*string_p)
  557.     free (*string_p);
  558.  
  559.       *string_p = (char *)NULL;
  560.       *string_size_p = 0;
  561.     }
  562.   else
  563.     {
  564.       if (strlen (string) >= *string_size_p)
  565.     *string_p = (char *)xrealloc
  566.       (*string_p, (*string_size_p = 1 + strlen (string)));
  567.  
  568.       strcpy (*string_p, string);
  569.     }
  570. }
  571.  
  572. /* Just like save_string (), but you also pass the length of STRING. */
  573. static void
  574. saven_string (string, len, string_p, string_size_p)
  575.      char *string;
  576.      int len;
  577.      char **string_p;
  578.      int *string_size_p;
  579. {
  580.   if (!string)
  581.     {
  582.       if (*string_p)
  583.     free (*string_p);
  584.  
  585.       *string_p = (char *)NULL;
  586.       *string_size_p = 0;
  587.     }
  588.   else
  589.     {
  590.       if (len >= *string_size_p)
  591.     *string_p = (char *)xrealloc (*string_p, (*string_size_p = 1 + len));
  592.  
  593.       strncpy (*string_p, string, len);
  594.       (*string_p)[len] = '\0';
  595.     }
  596. }
  597.  
  598. /* Return a pointer to the part of PATHNAME that simply defines the file. */
  599. char *
  600. filename_non_directory (pathname)
  601.      char *pathname;
  602. {
  603.   char *filename;
  604.  
  605.   filename = (char *)rindex (pathname, '/');
  606.  
  607.   if (filename)
  608.     filename++;
  609.   else
  610.     filename = pathname;
  611.  
  612.   return (filename);
  613. }
  614.  
  615. /* Return non-zero if NODE is one especially created by Info. */
  616. int
  617. internal_info_node_p (node)
  618.      NODE *node;
  619. {
  620.   if (node &&
  621.       (node->filename && !*node->filename) &&
  622.       !node->parent && node->nodename)
  623.     return (1);
  624.   else
  625.     return (0);
  626. }
  627.  
  628. /* Make NODE appear to be one especially created by Info. */
  629. void
  630. name_internal_node (node, name)
  631.      NODE *node;
  632.      char *name;
  633. {
  634.   if (!node)
  635.     return;
  636.  
  637.   node->filename = "";
  638.   node->parent = (char *)NULL;
  639.   node->nodename = name;
  640.   node->flags |= N_IsInternal;
  641. }
  642.  
  643. /* Return the window displaying NAME, the name of an internally created
  644.    Info window. */
  645. WINDOW *
  646. get_internal_info_window (name)
  647.      char *name;
  648. {
  649.   WINDOW *win = (WINDOW *)NULL;
  650.  
  651.   for (win = windows; win; win = win->next)
  652.     if (internal_info_node_p (win->node) &&
  653.     (strcmp (win->node->nodename, name) == 0))
  654.       break;
  655.  
  656.   return (win);
  657. }
  658.